package com.msda.service.room.Impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.msda.exception.database.DatabaseOperationException;
import com.msda.mapper.MeetingRoomMapper;
import com.msda.mapper.OrderMapper;
import com.msda.mapper.ReservationMapper;
import com.msda.pojo.dto.PageDTO;
import com.msda.pojo.dto.room.RoomDTO;
import com.msda.pojo.entity.order.Order;
import com.msda.pojo.entity.order.Reservation;
import com.msda.pojo.entity.requirement.MeetingRoomSearchParams;
import com.msda.pojo.entity.requirement.Requirement;
import com.msda.pojo.entity.room.Device;
import com.msda.pojo.entity.room.MeetingRoom;
import com.msda.pojo.vo.RoomOrderDetail;
import com.msda.service.room.RoomService;
import com.msda.utils.SnowFlakeID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.msda.constant.DatabaseConstant.*;
import static com.msda.utils.TimestampUtils.getDayLevelTimestamp;
import static com.msda.utils.TimestampUtils.getHourOfDay;

@Service
public class RoomServiceImpl implements RoomService {

    @Autowired
    private MeetingRoomMapper meetingRoomMapper;
    @Autowired
    private ReservationMapper reservationMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Override
    public String addRoom(RoomDTO roomDTO) {
        MeetingRoom room = new MeetingRoom();
        BeanUtil.copyProperties(roomDTO,room);
        room.setId(SnowFlakeID.getSnowflakeId());
        room.setCreatedDate(System.currentTimeMillis());
        QueryWrapper<MeetingRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",room.getName());
        Long j = meetingRoomMapper.selectCount(queryWrapper);
        if(j > 0){
            throw new DatabaseOperationException("有重名会议室！");
        }
        String id = room.getId();
        int i = meetingRoomMapper.insert(room);
        if(i < 1){
            throw new DatabaseOperationException(INSERT_DEFAULT);
        }
        return id;
    }

    @Override
    public MeetingRoom getRoomById(String roomId) {
        MeetingRoom room = meetingRoomMapper.selectById(roomId);
        return room;
    }

    @Override
    public int deleteRoom(String roomId) {
        int i = meetingRoomMapper.deleteById(roomId);
        if(i < 1){
            throw new DatabaseOperationException(DELETE_DEFAULT);
        }
        return i;
    }

    @Override
    public MeetingRoom updateRoom(RoomDTO roomDTO) {
        MeetingRoom room = meetingRoomMapper.selectById(roomDTO.getId());
        if(room == null){
            throw new DatabaseOperationException("会议室id错误！");
        }
        BeanUtil.copyProperties(roomDTO,room);
        QueryWrapper<MeetingRoom> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",room.getName());
        MeetingRoom meetingRoom = meetingRoomMapper.selectOne(queryWrapper);
        if(meetingRoom != null && !meetingRoom.getId().equals(room.getId())){
            throw new DatabaseOperationException("有重名会议室！");
        }
        int i =  meetingRoomMapper.updateById(room);
        if(i < 1){
            throw new DatabaseOperationException(UPDATE_DEFAULT);
        }
        return room;
    }

    @Override
    public Long getAllRoomCount() {
        QueryWrapper<MeetingRoom> queryWrapper = new QueryWrapper<>();
        Long i = 0L;
        i = meetingRoomMapper.selectCount(queryWrapper);
        return i;
    }

    @Override
    public List<MeetingRoom> getPageRoom(PageDTO pageDTO) {
        Page<MeetingRoom> page = new Page<>(pageDTO.getPage(),pageDTO.getSize());
        QueryWrapper<MeetingRoom> queryWrapper =new QueryWrapper<>();
        queryWrapper.orderByDesc("created_date");
        page = meetingRoomMapper.selectPage(page,queryWrapper);
        return page.getRecords();
    }

    @Override
    public void lockRoom(String roomId) {
        MeetingRoom meetingRoom = meetingRoomMapper.selectById(roomId);
        if(meetingRoom == null){
            throw new DatabaseOperationException("会议室不存在！");
        }
        meetingRoom.setLocked(true);
        meetingRoomMapper.updateById(meetingRoom);
    }

    @Override
    public void unLockRoom(String roomId) {
        MeetingRoom meetingRoom = meetingRoomMapper.selectById(roomId);
        if(meetingRoom == null){
            throw new DatabaseOperationException("会议室不存在！");
        }
        meetingRoom.setLocked(false);
        meetingRoomMapper.updateById(meetingRoom);
    }

    @Override
    public List<MeetingRoom> getRoomByRequirement(Requirement requirement) {
//        class Requirement {
//            capacity: number;	// 座位数量
//            reserveDate: number;// 预定日期
//            startTime: number;// 开始使用时间
//            endTime: number;// 结束使用时间
//            roomType: "classroom" | "roundtable";
//            device: {
//                speaker: boolean; // 是否有扬声器
//                internet: boolean; // 是否有互联网
//                projector: boolean; // 是否有投影仪
//            };
//            notes: string;// 其他需求可以写在备注中,但是不会作为筛选条件
//        }
        LambdaQueryWrapper<MeetingRoom> wrapper = new LambdaQueryWrapper<>();
        if(requirement.getDevice() != null){
            wrapper.apply(requirement.getDevice().isSpeaker(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.speaker')) = 'true'")
                    .apply(requirement.getDevice().isInternet(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.internet')) = 'true'")
                    .apply(requirement.getDevice().isProjector(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.projector')) = 'true'");
        }
        wrapper.ge(requirement.getCapacity() != null,MeetingRoom::getCapacity, requirement.getCapacity()).
                ge(requirement.getArea() != null,MeetingRoom::getArea, requirement.getArea()).
                eq(requirement.getRoomType() != null,MeetingRoom::getType, requirement.getRoomType()).
                ne(MeetingRoom::isMaintained,true).ne(MeetingRoom::isLocked,true)
                .orderByDesc(MeetingRoom::getCreatedDate);

        List<MeetingRoom> meetingRooms = meetingRoomMapper.selectList(wrapper);

        Set<String> roomId = getContractRoomByDate2(requirement);
        List<MeetingRoom> toRemove = new ArrayList<>();

        for (MeetingRoom room : meetingRooms) {
            if (roomId.contains(room.getId())) {
                toRemove.add(room);
            }
        }
        meetingRooms.removeAll(toRemove);
        return meetingRooms;
    }

//    private Set<String> getContractRoomByDate(Requirement requirement){
//        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
//        wrapper.apply("JSON_EXTRACT(requirement, '$.reserveDate') = {0}",requirement.getReserveDate())
//                .eq(Order::getPaymentStatus,"paid");
//        List<Order> orders = orderMapper.selectList(wrapper);
//        QueryWrapper<Reservation> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("status","confirmed");
//        List<Reservation> reservations = reservationMapper.selectList(queryWrapper);
//        List<Order> toRemoveOrder = new ArrayList<>();
//        Set<String> uniqueRoomId = new HashSet<>();
//        for(Reservation reservation : reservations){
//            uniqueRoomId.add(reservation.getOrderId());
//        }
//        for(Order order : orders){
//            if(!uniqueRoomId.contains(order.getId())){
//                toRemoveOrder.add(order);
//            }
//        }
//        orders.removeAll(toRemoveOrder);
//        Long startTime1 =requirement.getStartTime();
//        Long endTime1 =requirement.getStartTime();
//        uniqueRoomId.clear();
//        for(Order order : orders){
//            if(judgeTime(startTime1,endTime1,order.getRequirement().getStartTime(),order.getRequirement().getEndTime())){
//                continue;
//            }
//            uniqueRoomId.add(order.getRoomId());
//        }
//        return uniqueRoomId;
//
//    }
    private Set<String> getContractRoomByDate2(Requirement requirement) {
        // 查询已支付的订单
        Set<String> paidOrderIds = orderMapper.selectList(
                        new LambdaQueryWrapper<Order>()
                                .apply("JSON_EXTRACT(requirement, '$.reserveDate') = {0}", requirement.getReserveDate())
                                .eq(Order::getPaymentStatus, "paid")
                ).stream()
                .map(Order::getId)
                .collect(Collectors.toSet());
        // 查询已确认的预订订单ID
        Set<String> confirmedOrderIds = reservationMapper.selectList(
                        new QueryWrapper<Reservation>()
                                .eq("status", "confirmed")
                                .or().eq("status", "refund_verifying")
                ).stream()
                .map(Reservation::getOrderId)
                .collect(Collectors.toSet());
        // 获取交集订单ID（既已支付又已确认）
        paidOrderIds.retainAll(confirmedOrderIds);

        if(paidOrderIds.isEmpty()){
            return paidOrderIds;
        }
        // 直接查询符合条件的订单（避免加载所有字段）
        return orderMapper.selectList(
                        new LambdaQueryWrapper<Order>()
                                .in(Order::getId, paidOrderIds)
                                .select(Order::getRoomId, Order::getRequirement) // 只查询需要的字段
                ).stream()
                .filter(order -> !judgeTime(
                        requirement.getStartTime(),
                        requirement.getEndTime(),
                        order.getRequirement().getStartTime(),
                        order.getRequirement().getEndTime()
                ))
                .map(Order::getRoomId)
                .collect(Collectors.toSet());
    }

    @Override
    public List<MeetingRoom> adminSearchRoom(MeetingRoomSearchParams meetingRoomSearchParams) {
        LambdaQueryWrapper<MeetingRoom> wrapper = new LambdaQueryWrapper<>();
        Device device = meetingRoomSearchParams.getDevice();
        if(device != null){
            wrapper.apply(device.isSpeaker(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.speaker')) = 'true'")
                    .apply(device.isInternet(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.internet')) = 'true'")
                    .apply(device.isProjector(),
                            "JSON_UNQUOTE(JSON_EXTRACT(device, '$.projector')) = 'true'");
        }

        wrapper.ge(meetingRoomSearchParams.getCapacity() != null,MeetingRoom::getCapacity, meetingRoomSearchParams.getCapacity())
                .eq(meetingRoomSearchParams.getType() != null,MeetingRoom::getType, meetingRoomSearchParams.getType())
                .ge(meetingRoomSearchParams.getArea() != null,MeetingRoom::getArea, meetingRoomSearchParams.getArea())
                .like(meetingRoomSearchParams.getName() != null,MeetingRoom::getName, meetingRoomSearchParams.getName())
                .eq(meetingRoomSearchParams.getStatus() != null ,MeetingRoom::isMaintained, "normal".equals(meetingRoomSearchParams.getStatus()) ? 0 :1)
                .orderByDesc(MeetingRoom::getCreatedDate);

        List<MeetingRoom> meetingRooms = meetingRoomMapper.selectList(wrapper);
        return meetingRooms;
    }

    @Override
    public Object searchUsingMeetingRoom() {
        long now = System.currentTimeMillis();
        List<String> data =  meetingRoomMapper.searchUsingMeetingRoom(getDayLevelTimestamp(now),getHourOfDay(now));
        return data;
    }

    @Override
    public List<RoomOrderDetail> searchOrderByMeetingRoomId(String meetingRoomId) {
        List<RoomOrderDetail> roomOrderDetails = meetingRoomMapper.searchOrderDetailByRoomId(meetingRoomId);
        return roomOrderDetails;
    }

    //判断时间是否冲突
    private boolean judgeTime(Long startTime1,Long endTime1,Long startTime2,Long endTime2){
        return endTime1 <= startTime2 || endTime2 <= startTime1;
    }
}
